/**
 *  JFTSTool - Java Full Text Search tool.
 *  Copyright (C) <2009>  <grupo JFTS>
 *  This program is free software: you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation, either version 3 of the License, or
 *	(at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.jftstool.indexer.document.constructor;

import java.lang.reflect.Constructor;
import java.util.Iterator;
import java.util.List;

import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.jdom.Attribute;
import org.jdom.Element;
import org.jftstool.config.DocumentConfig;
import org.jftstool.config.FieldConfig;
import org.jftstool.config.JFTSConfiguration;
import org.jftstool.config.exceptions.JFTSConfigException;
import org.jftstool.indexer.document.exceptions.DocumentConstructorException;

/**
 * <p>Class responsible for constructing the index from document<br>
 * from an XML file returned by the DocumentFormater.</p> 
 * @author grupo JFTS
 *
 */
public class DocumentConstructor implements IDocumentConstructor {
	private DocumentConfig config = null;
	
	public DocumentConstructor() throws DocumentConstructorException {
		try {
			config = JFTSConfiguration.getInstance().getDocumentConfig();
		}
		catch (JFTSConfigException e) {
			throw new DocumentConstructorException(e.getMessage());
		}
	}
	
	@Override
	public Document buildJFTSDocument(org.jdom.Document documentXML) throws DocumentConstructorException {
			Document jftsDocument = new Document();
			boolean validDocument = true;
			if (documentXML == null){
				throw new DocumentConstructorException("Error, document can't be null");				
			}
			else{
				//Read node doc
				Element root = documentXML.getRootElement();
				
				if (root == null){
					throw new DocumentConstructorException("Error read xml, node doc not exists");					
				}
				else{
					//For any field in the validFields
					for (Object o: root.getChildren()){
						Element e = (Element) o;
						String text = e.getValue();
						Attribute a = e.getAttribute("name");
						
						_addFieldToDocument(a.getValue(), text, jftsDocument);
					}
					
					//Validate document
					validDocument = _validateDocument(jftsDocument);
					if (!validDocument){
						throw new DocumentConstructorException("Error validating document, some requiered field are mising");
					}
				}
			}
		return jftsDocument;
	}
	
	private boolean _validateDocument(Document jftsDocument){
		List<FieldConfig> validFields = config.getValidFields();
		Iterator<FieldConfig> it = validFields.iterator();
		boolean required = false;
		boolean validDocument = true;
		
		while (it.hasNext() && validDocument){
			FieldConfig fc = it.next();
			required = Boolean.parseBoolean(fc.getArg("required"));
			if (required){
				if (jftsDocument.getField(fc.getArg("name")) == null){
					validDocument = false;
				}
			}
		}
		return validDocument;
	}
	
	private void _addFieldToDocument(String name, String value, Document jftsDocument) throws DocumentConstructorException {
		if (name == null || name.isEmpty()){
			throw new DocumentConstructorException("Error, field name must have a value"); 
		}
		else{
			List<FieldConfig> validFields = config.getValidFields();
			boolean find = false;
			boolean indexed = false;		
			boolean stored = false;
			boolean buildTermVector = false;
			
			Iterator<FieldConfig> it = validFields.iterator();
			while (it.hasNext() && !find){
				FieldConfig fc = it.next();
				if(fc.getArg("name").equals(name)){
					indexed = Boolean.parseBoolean(fc.getArg("indexed"));				
					stored = Boolean.parseBoolean(fc.getArg("stored"));
					buildTermVector = Boolean.parseBoolean(fc.getArg("buildTermVector"));
					jftsDocument.add(_buildField(name, value, indexed, stored, buildTermVector));
					find = true;
				}
			}
			if (!find){
				boolean useDefaultField = config.getUseDefaultField();
				if (useDefaultField){
					FieldConfig defaultField = config.getDefaultField();					
					indexed = Boolean.parseBoolean(defaultField.getArg("indexed"));				
					stored = Boolean.parseBoolean(defaultField.getArg("stored"));
					buildTermVector = Boolean.parseBoolean(defaultField.getArg("buildTermVector"));					
					jftsDocument.add(_buildField(name, value, indexed, stored, buildTermVector));					
				}
				else{
					throw new DocumentConstructorException("Error, the field name: " + name  + " is invalid");
				}
			}
		}
	}
	
	private Field _buildField(String name, String value, boolean indexed, boolean stored, boolean buildTermVector) throws DocumentConstructorException {
		String className = config.getLuceneFieldClass();
		Field f = null;
		try {
			Class<? extends Field> fieldClass = Class.forName(className).asSubclass(Field.class);
			Constructor<? extends Field> c = fieldClass.getConstructor(String.class, String.class, Field.Store.class, Field.Index.class, Field.TermVector.class);
			f = c.newInstance(name, value, (stored == true) ? Field.Store.YES : Field.Store.NO, (indexed == true) ? Field.Index.ANALYZED : Field.Index.NOT_ANALYZED, (buildTermVector) ? Field.TermVector.YES : Field.TermVector.NO);
		}
		catch (Exception e) {
			throw new DocumentConstructorException("Error, cant't dinamic instantiate class: " + className);
		}		
		return f;
	}
}